home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
- */
-
- #ifndef lint
- static char *sccsid = "@(#)ex_cmdsub.c 7.8 (Berkeley) 3/9/87; 1.2 (Bellcore) 87/04/24";
- #endif not lint
-
- #include "ex.h"
- #include "ex_argv.h"
- #include "ex_temp.h"
- #include "ex_tty.h"
- #include "ex_vis.h"
-
- /*
- * Command mode subroutines implementing
- * append, args, copy, delete, join, move, put,
- * shift, tag, yank, z and undo
- */
-
- bool endline = 1;
- line *tad1;
- static jnoop();
-
- /*
- * Append after line a lines returned by function f.
- * Be careful about intermediate states to avoid scramble
- * if an interrupt comes in.
- */
- append(f, a)
- int (*f)();
- line *a;
- {
- register line *a1, *a2, *rdot;
- int nline;
-
- nline = 0;
- dot = a;
- if(FIXUNDO && !inopen && f!=getsub) {
- undap1 = undap2 = dot + 1;
- undkind = UNDCHANGE;
- }
- while ((*f)() == 0) {
- if (truedol >= endcore) {
- if (morelines() < 0) {
- if (FIXUNDO && f == getsub) {
- undap1 = addr1;
- undap2 = addr2 + 1;
- }
- error("Out of memory@- too many lines in file");
- }
- }
- nline++;
- a1 = truedol + 1;
- a2 = a1 + 1;
- dot++;
- undap2++;
- dol++;
- unddol++;
- truedol++;
- for (rdot = dot; a1 > rdot;)
- *--a2 = *--a1;
- *rdot = 0;
- putmark(rdot);
- if (f == gettty) {
- dirtcnt++;
- TSYNC();
- }
- }
- return (nline);
- }
-
- appendnone()
- {
-
- if(FIXUNDO) {
- undkind = UNDCHANGE;
- undap1 = undap2 = addr1;
- }
- }
-
- /*
- * Print out the argument list, with []'s around the current name.
- */
- pargs()
- {
- register char **av = argv0, *as = args0;
- register int ac;
-
- for (ac = 0; ac < argc0; ac++) {
- if (ac != 0)
- ex_putchar(' ' | QUOTE);
- if (ac + argc == argc0 - 1)
- ex_printf("[");
- lprintf("%s", as);
- if (ac + argc == argc0 - 1)
- ex_printf("]");
- as = av ? *++av : strend(as) + 1;
- }
- noonl();
- }
-
- /*
- * Delete lines; two cases are if we are really deleting,
- * more commonly we are just moving lines to the undo save area.
- */
- ex_delete(hush)
- bool hush;
- {
- register line *a1, *a2;
-
- nonzero();
- if(FIXUNDO) {
- register int (*dsavint)();
-
- #ifdef TRACE
- if (trace)
- vudump("before delete");
- #endif
- change();
- dsavint = signal(SIGINT, SIG_IGN);
- undkind = UNDCHANGE;
- a1 = addr1;
- squish();
- a2 = addr2;
- if (a2++ != dol) {
- reverse(a1, a2);
- reverse(a2, dol + 1);
- reverse(a1, dol + 1);
- }
- dol -= a2 - a1;
- unddel = a1 - 1;
- if (a1 > dol)
- a1 = dol;
- dot = a1;
- pkill[0] = pkill[1] = 0;
- signal(SIGINT, dsavint);
- #ifdef TRACE
- if (trace)
- vudump("after delete");
- #endif
- } else {
- register line *a3;
- register int i;
-
- change();
- a1 = addr1;
- a2 = addr2 + 1;
- a3 = truedol;
- i = a2 - a1;
- unddol -= i;
- undap2 -= i;
- dol -= i;
- truedol -= i;
- do
- *a1++ = *a2++;
- while (a2 <= a3);
- a1 = addr1;
- if (a1 > dol)
- a1 = dol;
- dot = a1;
- }
- if (!hush)
- killed();
- }
-
- deletenone()
- {
-
- if(FIXUNDO) {
- undkind = UNDCHANGE;
- squish();
- unddel = addr1;
- }
- }
-
- /*
- * Crush out the undo save area, moving the open/visual
- * save area down in its place.
- */
- squish()
- {
- register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
-
- if(FIXUNDO) {
- if (inopen == -1)
- return;
- if (a1 < a2 && a2 < a3)
- do
- *a1++ = *a2++;
- while (a2 < a3);
- truedol -= unddol - dol;
- unddol = dol;
- }
- }
-
- /*
- * Join lines. Special hacks put in spaces, two spaces if
- * preceding line ends with '.', or no spaces if next line starts with ).
- */
- static int jcount, jnoop();
-
- join(c)
- int c;
- {
- register line *a1;
- register char *cp, *cp1;
-
- cp = genbuf;
- *cp = 0;
- for (a1 = addr1; a1 <= addr2; a1++) {
- getline(*a1);
- cp1 = linebuf;
- if (a1 != addr1 && c == 0) {
- while (*cp1 == ' ' || *cp1 == '\t')
- cp1++;
- if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
- if (*cp1 != ')') {
- *cp++ = ' ';
- if (cp[-2] == '.')
- *cp++ = ' ';
- }
- }
- }
- while (*cp++ = *cp1++)
- if (cp > &genbuf[LBSIZE-2])
- error("Line overflow|Result line of join would be too long");
- cp--;
- }
- strcLIN(genbuf);
- ex_delete(0);
- jcount = 1;
- if (FIXUNDO)
- undap1 = undap2 = addr1;
- ignore(append(jnoop, --addr1));
- if (FIXUNDO)
- vundkind = VMANY;
- }
-
- static
- jnoop()
- {
-
- return(--jcount);
- }
-
- /*
- * Move and copy lines. Hard work is done by move1 which
- * is also called by undo.
- */
- int getcopy();
-
- move()
- {
- register line *adt;
- bool iscopy = 0;
-
- if (Command[0] == 'm') {
- setdot1();
- markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
- } else {
- iscopy++;
- setdot();
- }
- nonzero();
- adt = address((char*)0);
- if (adt == 0)
- serror("%s where?|%s requires a trailing address", Command);
- newline();
- move1(iscopy, adt);
- killed();
- }
-
- move1(cflag, addrt)
- int cflag;
- line *addrt;
- {
- register line *adt, *ad1, *ad2;
- int lines;
-
- adt = addrt;
- lines = (addr2 - addr1) + 1;
- if (cflag) {
- tad1 = addr1;
- ad1 = dol;
- ignore(append(getcopy, ad1++));
- ad2 = dol;
- } else {
- ad2 = addr2;
- for (ad1 = addr1; ad1 <= ad2;)
- *ad1++ &= ~01;
- ad1 = addr1;
- }
- ad2++;
- if (adt < ad1) {
- if (adt + 1 == ad1 && !cflag && !inglobal)
- error("That move would do nothing!");
- dot = adt + (ad2 - ad1);
- if (++adt != ad1) {
- reverse(adt, ad1);
- reverse(ad1, ad2);
- reverse(adt, ad2);
- }
- } else if (adt >= ad2) {
- dot = adt++;
- reverse(ad1, ad2);
- reverse(ad2, adt);
- reverse(ad1, adt);
- } else
- error("Move to a moved line");
- change();
- if (!inglobal)
- if(FIXUNDO) {
- if (cflag) {
- undap1 = addrt + 1;
- undap2 = undap1 + lines;
- deletenone();
- } else {
- undkind = UNDMOVE;
- undap1 = addr1;
- undap2 = addr2;
- unddel = addrt;
- squish();
- }
- }
- }
-
- getcopy()
- {
-
- if (tad1 > addr2)
- return (EOF);
- getline(*tad1++);
- return (0);
- }
-
- /*
- * Put lines in the buffer from the undo save area.
- */
- getput()
- {
-
- if (tad1 > unddol)
- return (EOF);
- getline(*tad1++);
- tad1++;
- return (0);
- }
-
- put()
- {
- register int cnt;
-
- if (!FIXUNDO)
- error("Cannot put inside global/macro");
- cnt = unddol - dol;
- if (cnt && inopen && pkill[0] && pkill[1]) {
- pragged(1);
- return;
- }
- tad1 = dol + 1;
- ignore(append(getput, addr2));
- undkind = UNDPUT;
- notecnt = cnt;
- netchange(cnt);
- }
-
- /*
- * A tricky put, of a group of lines in the middle
- * of an existing line. Only from open/visual.
- * Argument says pkills have meaning, e.g. called from
- * put; it is 0 on calls from putreg.
- */
- pragged(kill)
- bool kill;
- {
- extern char *cursor;
- register char *gp = &genbuf[cursor - linebuf];
-
- /*
- * This kind of stuff is TECO's forte.
- * We just grunge along, since it cuts
- * across our line-oriented model of the world
- * almost scrambling our addled brain.
- */
- if (!kill)
- getDOT();
- strcpy(genbuf, linebuf);
- getline(*unddol);
- if (kill)
- *pkill[1] = 0;
- strcat(linebuf, gp);
- putmark(unddol);
- getline(dol[1]);
- if (kill)
- strcLIN(pkill[0]);
- strcpy(gp, linebuf);
- strcLIN(genbuf);
- putmark(dol+1);
- undkind = UNDCHANGE;
- undap1 = dot;
- undap2 = dot + 1;
- unddel = dot - 1;
- undo(1);
- }
-
- /*
- * Shift lines, based on c.
- * If c is neither < nor >, then this is a lisp aligning =.
- */
- shift(c, cnt)
- int c;
- int cnt;
- {
- register line *addr;
- register char *cp;
- char *dp;
- register int i;
-
- if(FIXUNDO)
- save12(), undkind = UNDCHANGE;
- cnt *= value(SHIFTWIDTH);
- for (addr = addr1; addr <= addr2; addr++) {
- dot = addr;
- #ifdef LISPCODE
- if (c == '=' && addr == addr1 && addr != addr2)
- continue;
- #endif
- getDOT();
- i = whitecnt(linebuf);
- switch (c) {
-
- case '>':
- if (linebuf[0] == 0)
- continue;
- cp = genindent(i + cnt);
- break;
-
- case '<':
- if (i == 0)
- continue;
- i -= cnt;
- cp = i > 0 ? genindent(i) : genbuf;
- break;
-
- #ifdef LISPCODE
- default:
- i = lindent(addr);
- getDOT();
- cp = genindent(i);
- break;
- #endif
- }
- if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
- error("Line too long|Result line after shift would be too long");
- CP(cp, dp);
- strcLIN(genbuf);
- putmark(addr);
- }
- killed();
- }
-
- /*
- * Find a tag in the tags file.
- * Most work here is in parsing the tags file itself.
- */
- tagfind(quick)
- bool quick;
- {
- char cmdbuf[BUFSIZ];
- char filebuf[FNSIZE];
- char tagfbuf[128];
- register int c, d;
- bool samef = 1;
- int tfcount = 0;
- int omagic;
- char *fn, *fne;
- struct stat sbuf;
- #ifdef FASTTAG
- int iof;
- char iofbuf[MAXBSIZE];
- long mid; /* assumed byte offset */
- long top, bot; /* length of tag file */
- #endif
-
- omagic = value(MAGIC);
- if (!skipend()) {
- register char *lp = lasttag;
-
- while (!iswhite(peekchar()) && !endcmd(peekchar()))
- if (lp < &lasttag[sizeof lasttag - 2])
- *lp++ = ex_getchar();
- else
- ignchar();
- *lp++ = 0;
- if (!endcmd(peekchar()))
- badtag:
- error("Bad tag|Give one tag per line");
- } else if (lasttag[0] == 0)
- error("No previous tag");
- c = ex_getchar();
- if (!endcmd(c))
- goto badtag;
- if (c == EOF)
- ungetchar(c);
- clrstats();
-
- /*
- * Loop once for each file in tags "path".
- */
- CP(tagfbuf, svalue(TAGS));
- fne = tagfbuf - 1;
- while (fne) {
- fn = ++fne;
- while (*fne && *fne != ' ')
- fne++;
- if (*fne == 0)
- fne = 0; /* done, quit after this time */
- else
- *fne = 0; /* null terminate filename */
- #ifdef FASTTAG
- iof = topen(fn, iofbuf);
- if (iof == -1)
- continue;
- tfcount++;
- fstat(iof, &sbuf);
- top = sbuf.st_size;
- if (top == 0L )
- top = -1L;
- bot = 0L;
- while (top >= bot) {
- #else
- /*
- * Avoid stdio and scan tag file linearly.
- */
- io = open(fn, 0);
- if (io<0)
- continue;
- tfcount++;
- if (fstat(io, &sbuf) < 0)
- bsize = LBSIZE;
- else {
- bsize = sbuf.st_blksize;
- if (bsize <= 0)
- bsize = LBSIZE;
- }
- while (getfile() == 0) {
- #endif
- /* loop for each tags file entry */
- register char *cp = linebuf;
- register char *lp = lasttag;
- char *oglobp;
-
- #ifdef FASTTAG
- mid = (top + bot) / 2;
- tseek(iof, mid);
- if (mid > 0) /* to get first tag in file to work */
- /* scan to next \n */
- if(tgets(linebuf, sizeof linebuf, iof)==NULL)
- goto goleft;
- /* get the line itself */
- if(tgets(linebuf, sizeof linebuf, iof)==NULL)
- goto goleft;
- #ifdef TDEBUG
- ex_printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
- #endif
- #endif
- while (*cp && *lp == *cp)
- cp++, lp++;
- if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 ||
- lp-lasttag < value(TAGLENGTH))) {
- #ifdef FASTTAG
- if (*lp > *cp)
- bot = mid + 1;
- else
- goleft:
- top = mid - 1;
- #endif
- /* Not this tag. Try the next */
- continue;
- }
-
- /*
- * We found the tag. Decode the line in the file.
- */
- #ifdef FASTTAG
- tclose(iof);
- #else
- close(io);
- #endif
- /* Rest of tag if abbreviated */
- while (!iswhite(*cp))
- cp++;
-
- /* name of file */
- while (*cp && iswhite(*cp))
- cp++;
- if (!*cp)
- badtags:
- serror("%s: Bad tags file entry", lasttag);
- lp = filebuf;
- while (*cp && *cp != ' ' && *cp != '\t') {
- if (lp < &filebuf[sizeof filebuf - 2])
- *lp++ = *cp;
- cp++;
- }
- *lp++ = 0;
-
- if (*cp == 0)
- goto badtags;
- if (dol != zero) {
- /*
- * Save current position in 't for ^^ in visual.
- */
- names['t'-'a'] = *dot &~ 01;
- if (inopen) {
- extern char *ncols['z'-'a'+2];
- extern char *cursor;
-
- ncols['t'-'a'] = cursor;
- }
- }
- strcpy(cmdbuf, cp);
- if (strcmp(filebuf, savedfile) || !edited) {
- char cmdbuf2[sizeof filebuf + 10];
-
- /* Different file. Do autowrite & get it. */
- if (!quick) {
- ckaw();
- if (chng && dol > zero)
- error("No write@since last change (:tag! overrides)");
- }
- oglobp = globp;
- strcpy(cmdbuf2, "e! ");
- strcat(cmdbuf2, filebuf);
- globp = cmdbuf2;
- d = peekc; ungetchar(0);
- commands(1, 1);
- peekc = d;
- globp = oglobp;
- value(MAGIC) = omagic;
- samef = 0;
- }
-
- /*
- * Look for pattern in the current file.
- */
- oglobp = globp;
- globp = cmdbuf;
- d = peekc; ungetchar(0);
- if (samef)
- markpr(dot);
- /*
- * BUG: if it isn't found (user edited header
- * line) we get left in nomagic mode.
- */
- value(MAGIC) = 0;
- commands(1, 1);
- peekc = d;
- globp = oglobp;
- value(MAGIC) = omagic;
- return;
- } /* end of "for each tag in file" */
-
- /*
- * No such tag in this file. Close it and try the next.
- */
- #ifdef FASTTAG
- tclose(iof);
- #else
- close(io);
- #endif
- } /* end of "for each file in path" */
- if (tfcount <= 0)
- error("No tags file");
- else
- serror("%s: No such tag@in tags file", lasttag);
- }
-
- /*
- * Save lines from addr1 thru addr2 as though
- * they had been deleted.
- */
- yank()
- {
-
- if (!FIXUNDO)
- error("Can't yank inside global/macro");
- save12();
- undkind = UNDNONE;
- killcnt(addr2 - addr1 + 1);
- }
-
- /*
- * z command; print windows of text in the file.
- *
- * If this seems unreasonably arcane, the reasons
- * are historical. This is one of the first commands
- * added to the first ex (then called en) and the
- * number of facilities here were the major advantage
- * of en over ed since they allowed more use to be
- * made of fast terminals w/o typing .,.22p all the time.
- */
- bool zhadpr;
- bool znoclear;
- short zweight;
-
- zop(hadpr)
- int hadpr;
- {
- register int c, lines, op;
- bool excl;
-
- zhadpr = hadpr;
- notempty();
- znoclear = 0;
- zweight = 0;
- excl = exclam();
- switch (c = op = ex_getchar()) {
-
- case '^':
- zweight = 1;
- case '-':
- case '+':
- while (peekchar() == op) {
- ignchar();
- zweight++;
- }
- case '=':
- case '.':
- c = ex_getchar();
- break;
-
- case EOF:
- znoclear++;
- break;
-
- default:
- op = 0;
- break;
- }
- if (isdigit(c)) {
- lines = c - '0';
- for(;;) {
- c = ex_getchar();
- if (!isdigit(c))
- break;
- lines *= 10;
- lines += c - '0';
- }
- if (lines < LINES)
- znoclear++;
- value(WINDOW) = lines;
- if (op == '=')
- lines += 2;
- } else
- lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
- if (inopen || c != EOF) {
- ungetchar(c);
- newline();
- }
- addr1 = addr2;
- if (addr2 == 0 && dot < dol && op == 0)
- addr1 = addr2 = dot+1;
- setdot();
- zop2(lines, op);
- }
-
- zop2(lines, op)
- register int lines;
- register int op;
- {
- register line *split;
-
- split = NULL;
- switch (op) {
-
- case EOF:
- if (addr2 == dol)
- error("\nAt EOF");
- case '+':
- if (addr2 == dol)
- error("At EOF");
- addr2 += lines * zweight;
- if (addr2 > dol)
- error("Hit BOTTOM");
- addr2++;
- default:
- addr1 = addr2;
- addr2 += lines-1;
- dot = addr2;
- break;
-
- case '=':
- case '.':
- znoclear = 0;
- lines--;
- lines >>= 1;
- if (op == '=')
- lines--;
- addr1 = addr2 - lines;
- if (op == '=')
- dot = split = addr2;
- addr2 += lines;
- if (op == '.') {
- markDOT();
- dot = addr2;
- }
- break;
-
- case '^':
- case '-':
- addr2 -= lines * zweight;
- if (addr2 < one)
- error("Hit TOP");
- lines--;
- addr1 = addr2 - lines;
- dot = addr2;
- break;
- }
- if (addr1 <= zero)
- addr1 = one;
- if (addr2 > dol)
- addr2 = dol;
- if (dot > dol)
- dot = dol;
- if (addr1 > addr2)
- return;
- if (op == EOF && zhadpr) {
- getline(*addr1);
- ex_putchar('\r' | QUOTE);
- shudclob = 1;
- } else if (znoclear == 0 && CL != NOSTR && !inopen) {
- flush1();
- vclear();
- }
- if (addr2 - addr1 > 1)
- pstart();
- if (split) {
- plines(addr1, split - 1, 0);
- splitit();
- plines(split, split, 0);
- splitit();
- addr1 = split + 1;
- }
- plines(addr1, addr2, 0);
- }
-
- static
- splitit()
- {
- register int l;
-
- for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
- ex_putchar('-');
- putnl();
- }
-
- plines(adr1, adr2, movedot)
- line *adr1;
- register line *adr2;
- bool movedot;
- {
- register line *addr;
-
- pofix();
- for (addr = adr1; addr <= adr2; addr++) {
- getline(*addr);
- pline(lineno(addr));
- if (inopen)
- ex_putchar('\n' | QUOTE);
- if (movedot)
- dot = addr;
- }
- }
-
- pofix()
- {
-
- if (inopen && Outchar != termchar) {
- vnfl();
- setoutt();
- }
- }
-
- /*
- * Dudley doright to the rescue.
- * Undo saves the day again.
- * A tip of the hatlo hat to Warren Teitleman
- * who made undo as useful as do.
- *
- * Command level undo works easily because
- * the editor has a unique temporary file
- * index for every line which ever existed.
- * We don't have to save large blocks of text,
- * only the indices which are small. We do this
- * by moving them to after the last line in the
- * line buffer array, and marking down info
- * about whence they came.
- *
- * Undo is its own inverse.
- */
- undo(c)
- bool c;
- {
- register int i;
- register line *jp, *kp;
- line *dolp1, *newdol, *newadot;
-
- #ifdef TRACE
- if (trace)
- vudump("before undo");
- #endif
- if (inglobal && inopen <= 0)
- error("Can't undo in global@commands");
- if (!c)
- somechange();
- pkill[0] = pkill[1] = 0;
- change();
- if (undkind == UNDMOVE) {
- /*
- * Command to be undone is a move command.
- * This is handled as a special case by noting that
- * a move "a,b m c" can be inverted by another move.
- */
- if ((i = (jp = unddel) - undap2) > 0) {
- /*
- * when c > b inverse is a+(c-b),c m a-1
- */
- addr2 = jp;
- addr1 = (jp = undap1) + i;
- unddel = jp-1;
- } else {
- /*
- * when b > c inverse is c+1,c+1+(b-a) m b
- */
- addr1 = ++jp;
- addr2 = jp + ((unddel = undap2) - undap1);
- }
- kp = undap1;
- move1(0, unddel);
- dot = kp;
- Command = "move";
- killed();
- } else {
- int cnt;
-
- newadot = dot;
- cnt = lineDOL();
- newdol = dol;
- dolp1 = dol + 1;
- /*
- * Command to be undone is a non-move.
- * All such commands are treated as a combination of
- * a delete command and a append command.
- * We first move the lines appended by the last command
- * from undap1 to undap2-1 so that they are just before the
- * saved deleted lines.
- */
- if ((i = (kp = undap2) - (jp = undap1)) > 0) {
- if (kp != dolp1) {
- reverse(jp, kp);
- reverse(kp, dolp1);
- reverse(jp, dolp1);
- }
- /*
- * Account for possible backward motion of target
- * for restoration of saved deleted lines.
- */
- if (unddel >= jp)
- unddel -= i;
- newdol -= i;
- /*
- * For the case where no lines are restored, dot
- * is the line before the first line deleted.
- */
- dot = jp-1;
- }
- /*
- * Now put the deleted lines, if any, back where they were.
- * Basic operation is: dol+1,unddol m unddel
- */
- if (undkind == UNDPUT) {
- unddel = undap1 - 1;
- squish();
- }
- jp = unddel + 1;
- if ((i = (kp = unddol) - dol) > 0) {
- if (jp != dolp1) {
- reverse(jp, dolp1);
- reverse(dolp1, ++kp);
- reverse(jp, kp);
- }
- /*
- * Account for possible forward motion of the target
- * for restoration of the deleted lines.
- */
- if (undap1 >= jp)
- undap1 += i;
- /*
- * Dot is the first resurrected line.
- */
- dot = jp;
- newdol += i;
- }
- /*
- * Clean up so we are invertible
- */
- unddel = undap1 - 1;
- undap1 = jp;
- undap2 = jp + i;
- dol = newdol;
- netchHAD(cnt);
- if (undkind == UNDALL) {
- dot = undadot;
- undadot = newadot;
- } else
- undkind = UNDCHANGE;
- }
- /*
- * Defensive programming - after a munged undadot.
- * Also handle empty buffer case.
- */
- if ((dot <= zero || dot > dol) && dot != dol)
- dot = one;
- #ifdef TRACE
- if (trace)
- vudump("after undo");
- #endif
- }
-
- /*
- * Be (almost completely) sure there really
- * was a change, before claiming to undo.
- */
- somechange()
- {
- register line *ip, *jp;
-
- switch (undkind) {
-
- case UNDMOVE:
- return;
-
- case UNDCHANGE:
- if (undap1 == undap2 && dol == unddol)
- break;
- return;
-
- case UNDPUT:
- if (undap1 != undap2)
- return;
- break;
-
- case UNDALL:
- if (unddol - dol != lineDOL())
- return;
- for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
- if ((*ip &~ 01) != (*jp &~ 01))
- return;
- break;
-
- case UNDNONE:
- error("Nothing to undo");
- }
- error("Nothing changed|Last undoable command didn't change anything");
- }
-
- /*
- * Map command:
- * map src dest
- */
- mapcmd(un, ab)
- int un; /* true if this is unmap command */
- int ab; /* true if this is abbr command */
- {
- char lhs[100], rhs[100]; /* max sizes resp. */
- register char *p;
- register int c; /* mjm: char --> int */
- char *dname;
- struct maps *mp; /* the map structure we are working on */
-
- mp = ab ? abbrevs : exclam() ? immacs : arrows;
- if (skipend()) {
- int i;
-
- /* print current mapping values */
- if (peekchar() != EOF)
- ignchar();
- if (un)
- error("Missing lhs");
- if (inopen)
- pofix();
- for (i=0; mp[i].mapto; i++)
- if (mp[i].cap) {
- lprintf("%s", mp[i].descr);
- ex_putchar('\t');
- lprintf("%s", mp[i].cap);
- ex_putchar('\t');
- lprintf("%s", mp[i].mapto);
- putNFL();
- }
- return;
- }
-
- ignore(skipwh());
- for (p=lhs; ; ) {
- c = ex_getchar();
- if (c == CTRL(v)) {
- c = ex_getchar();
- } else if (!un && any(c, " \t")) {
- /* End of lhs */
- break;
- } else if (endcmd(c) && c!='"') {
- ungetchar(c);
- if (un) {
- newline();
- *p = 0;
- addmac(lhs, NOSTR, NOSTR, mp);
- return;
- } else
- error("Missing rhs");
- }
- *p++ = c;
- }
- *p = 0;
-
- if (skipend())
- error("Missing rhs");
- for (p=rhs; ; ) {
- c = ex_getchar();
- if (c == CTRL(v)) {
- c = ex_getchar();
- } else if (endcmd(c) && c!='"') {
- ungetchar(c);
- break;
- }
- *p++ = c;
- }
- *p = 0;
- newline();
- /*
- * Special hack for function keys: #1 means key f1, etc.
- * If the terminal doesn't have function keys, we just use #1.
- */
- if (lhs[0] == '#') {
- char *fnkey;
- char *fkey();
- char funkey[3];
-
- fnkey = fkey(lhs[1] - '0');
- funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
- if (fnkey)
- strcpy(lhs, fnkey);
- dname = funkey;
- } else {
- dname = lhs;
- }
- addmac(lhs,rhs,dname,mp);
- }
-
- /*
- * Add a macro definition to those that already exist. The sequence of
- * chars "src" is mapped into "dest". If src is already mapped into something
- * this overrides the mapping. There is no recursion. Unmap is done by
- * using NOSTR for dest. Dname is what to show in listings. mp is
- * the structure to affect (arrows, etc).
- */
- addmac(src,dest,dname,mp)
- register char *src, *dest, *dname;
- register struct maps *mp;
- {
- register int slot, zer;
-
- #ifdef TRACE
- if (trace)
- fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
- #endif
- if (dest && mp==arrows) {
- /* Make sure user doesn't screw himself */
- /*
- * Prevent tail recursion. We really should be
- * checking to see if src is a suffix of dest
- * but this makes mapping involving escapes that
- * is reasonable mess up.
- */
- if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
- error("No tail recursion");
- /*
- * We don't let the user rob himself of ":", and making
- * multi char words is a bad idea so we don't allow it.
- * Note that if user sets mapinput and maps all of return,
- * linefeed, and escape, he can screw himself. This is
- * so weird I don't bother to check for it.
- */
- if (isalpha(src[0]) && src[1] || any(src[0],":"))
- error("Too dangerous to map that");
- }
- else if (dest) {
- /* check for tail recursion in input mode: fussier */
- if (eq(src, dest+strlen(dest)-strlen(src)))
- error("No tail recursion");
- }
- /*
- * If the src were null it would cause the dest to
- * be mapped always forever. This is not good.
- */
- if (src == NOSTR || src[0] == 0)
- error("Missing lhs");
-
- /* see if we already have a def for src */
- zer = -1;
- for (slot=0; mp[slot].mapto; slot++) {
- if (mp[slot].cap) {
- if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
- break; /* if so, reuse slot */
- } else {
- zer = slot; /* remember an empty slot */
- }
- }
-
- if (dest == NOSTR) {
- /* unmap */
- if (mp[slot].cap) {
- mp[slot].cap = NOSTR;
- mp[slot].descr = NOSTR;
- } else {
- error("Not mapped|That macro wasn't mapped");
- }
- return;
- }
-
- /* reuse empty slot, if we found one and src isn't already defined */
- if (zer >= 0 && mp[slot].mapto == 0)
- slot = zer;
-
- /* if not, append to end */
- if (slot >= MAXNOMACS)
- error("Too many macros");
- if (msnext == 0) /* first time */
- msnext = mapspace;
- /* Check is a bit conservative, we charge for dname even if reusing src */
- if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
- error("Too much macro text");
- CP(msnext, src);
- mp[slot].cap = msnext;
- msnext += strlen(src) + 1; /* plus 1 for null on the end */
- CP(msnext, dest);
- mp[slot].mapto = msnext;
- msnext += strlen(dest) + 1;
- if (dname) {
- CP(msnext, dname);
- mp[slot].descr = msnext;
- msnext += strlen(dname) + 1;
- } else {
- /* default descr to string user enters */
- mp[slot].descr = src;
- }
- }
-
- /*
- * Implements macros from command mode. c is the buffer to
- * get the macro from.
- */
- cmdmac(c)
- char c;
- {
- char macbuf[BUFSIZ];
- line *ad, *a1, *a2;
- char *oglobp;
- short pk;
- bool oinglobal;
-
- lastmac = c;
- oglobp = globp;
- oinglobal = inglobal;
- pk = peekc; peekc = 0;
- if (inglobal < 2)
- inglobal = 1;
- regbuf(c, macbuf, sizeof(macbuf));
- a1 = addr1; a2 = addr2;
- for (ad=a1; ad<=a2; ad++) {
- globp = macbuf;
- dot = ad;
- commands(1,1);
- }
- globp = oglobp;
- inglobal = oinglobal;
- peekc = pk;
- }
-